/**
 * @Copyright:Copyright (c) 2007 - 2023
 * 
 */
package com.tengtong.duang.app.query.core;

import java.util.regex.Matcher;

import com.tengtong.commons.base.KeyValue;
import com.tengtong.commons.lang.ExceptionUtils;
import com.tengtong.commons.lang.RegexUtils;
import com.tengtong.commons.lang.StringBuilderX;
import com.tengtong.commons.lang.StringUtils;

/**
 * 构建表达式的接口
 *
 * @author lin_jinming
 * @since: v1.0
 */
public interface ConditionExpressBuilder {

    /**
     * 构建表达式
     *
     * @param kv
     * @return
     */
    public String build(KeyValue kv);
    
    /**
     * 构建表达式
     *
     * @param kv
     * @return
     */
    public String build(FieldValueExpress kv);
    
    /**
     * 对字段进行处理的方法
     *
     * @param f
     * @return
     */
    default public String processField(String f) {
        return f;
    }
    
    /**
     * 值的处理
     *
     * @param v
     * @return
     */
    default public String processValue(String v) {
        return v;
    }
    
    
    /**
     * 一个简单的接口实现
     *
     *
     * @author lin_jinming
     * @since: v1.0
     */
    public static class SimpleExpressBuilder implements ConditionExpressBuilder {

        /* (non-Javadoc)
         * @see com.tengtong.duang.app.query.core.ConditionExpressBuilder#build(com.tengtong.commons.base.KeyValue)
         */
        @Override
        public String build(KeyValue kv) {
            Object v = kv.getValue();
            if(v != null) {
                if(v instanceof String sv) {
                    String valueStr = sv.trim();
                    Matcher matcher = RegexUtils.FUNC_PATTERN.matcher(valueStr);
                    if(matcher.find() && matcher.groupCount() == 2) {
                        var funcName = matcher.group(1);
                        var funcParams = matcher.group(2);
                        
                        var op = ExpressFunc.fromValue(funcName);
                        if(op != null) {
                            StringBuilderX sb = StringBuilderX.of();
                            return switch (op) {
                                case Like   -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" LIKE '%")
                                      .append(processValue(funcParams))
                                      .append("%'");
                                    yield sb.toString(); 
                                }
                                case StartWith -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" LIKE '")
                                      .append(processValue(funcParams))
                                      .append("%'");
                                      
                                    yield sb.toString();
                                }
                                case EndWith    ->{
                                    sb.append(processField(kv.getKey()))
                                      .append(" LIKE '%")
                                      .append(processValue(funcParams))
                                      .append("'");
                                    yield sb.toString();
                                }
                                case In     -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" IN (")
                                      .append(processValue(funcParams))
                                      .append(")");
                                    yield sb.toString();
                                }
                                case NotIn  -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" NOT IN (")
                                      .append(processValue(funcParams))
                                      .append(")");
                                    yield sb.toString();
                                }
                                case IsNull -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" IS NULL ");
                                    yield sb.toString();
                                }
                                case NotNull -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" IS NOT NULL ");
                                    
                                    yield sb.toString();
                                }
                                default     -> {
                                    sb.append(processField(kv.getKey()))
                                      .append(" ")
                                      .append(op.getOp())
                                      .append(" ")
                                      .append(processValue(funcParams));
                                    yield sb.toString();
                                }
                            };                        
                        } else {
                            throw ExceptionUtils.ofRuntime("语法错误|不认识的操作: %s", op);
                        }
                    } else {
                        // 其他内容，例如直接相等，或区间 [] 等不带函数的条件
                        var name = kv.getKey().trim();
                        if(name.charAt(0) == '[') {
                            return between(kv.getKey(), valueStr);
                        } else {
                            return kv.toString();
                        }
                    }

                } else if(v instanceof Number nv) {
                    return kv.toString();
                }
            } else {
                // 抛出异常：条件表达式不能为 null
                throw ExceptionUtils.ofRuntime("语法错误|不认识的数据：%s", kv);
            }
            
            return kv.toString();            
        }
                
        private static String between(String name, String value) {
            if(value.charAt(0) == '[' && value.charAt(value.length()-1) == ']') {
                String[] ss = StringUtils.splitByComma(value.substring(1, value.length()-1));
                return name + " BETWEEN '" + ss[0].trim() + "' AND '" + ss[1].trim() + "'";
            }
            return "";
        }

        /* (non-Javadoc)
         * @see com.tengtong.duang.app.query.core.ConditionExpressBuilder#build(com.tengtong.duang.app.query.core.FieldValueExpress)
         */
        @Override
        public String build(FieldValueExpress kv) {
            
            StringBuilderX sb = StringBuilderX.of();
            return switch (kv.func()) {
                case Like   -> {
                    sb.append(processField(kv.name()))
                      .append(" LIKE '%")
                      .append(processValue(kv.getValue()))
                      .append("%'");
                    yield sb.toString(); 
                }
                case StartWith -> {
                    sb.append(processField(kv.name()))
                      .append(" LIKE '")
                      .append(processValue(kv.getValue()))
                      .append("%'");
                      
                    yield sb.toString();
                }
                case EndWith    ->{
                    sb.append(processField(kv.name()))
                      .append(" LIKE '%")
                      .append(processValue(kv.getValue()))
                      .append("'");
                    yield sb.toString();
                }
                case In     -> {
                    sb.append(processField(kv.name()))
                      .append(" IN (")
                      .append(processValue(kv.getValue()))
                      .append(")");
                    yield sb.toString();
                }
                case NotIn  -> {
                    sb.append(processField(kv.name()))
                      .append(" NOT IN (")
                      .append(processValue(kv.getValue()))
                      .append(")");
                    yield sb.toString();
                }
                case IsNull -> {
                    sb.append(processField(kv.name()))
                      .append(" IS NULL ");
                    yield sb.toString();
                }
                case NotNull -> {
                    sb.append(processField(kv.name()))
                      .append(" IS NOT NULL ");
                    
                    yield sb.toString();
                }
                case Between -> {
                    String[] ss = StringUtils.splitByComma(kv.getValue());
                    sb.append(processField(kv.name()))
                      .append(" BETWEEN '")
                      .append(ss[0].trim())
                      .append("' AND '")
                      .append(ss[1].trim())
                      .append("'");
                    
                    //return name + " BETWEEN '" + ss[0].trim() + "' AND '" + ss[1].trim() + "'";
                    yield sb.toString();
                }
                default     -> {
                    sb.append(processField(kv.name()))
                      .append(" ")
                      .append(kv.func().getOp())
                      .append(" ")
                      .append(processValue(kv.getValue()));
                    yield sb.toString();
                }
            };
        }
    }
}
